Now the background app must retrieve/receive data from the patch. Figures 2a and 2b illustrate the interaction using two approaches.
Figure 2a. Interaction using Gestalt().
Figure 2a illustrates a polling model. Here, the background app requests the address of the patch's Gestalt handler, which the patch advertises via NewGestalt(). Then, the background app uses a selector value to request the next element from the patch.
Figure 2b. Interaction using shared memory.
In Figure 2b, the background app and patch share a set of data structures. The patch retrieves the next empty element, fills it, and returns it to the pool. The background app retrieves the next filled element. There is no need for direct communication, except to setup the shared memory area. Determining whether an element is empty or not can be done by checking its values. The background app needs to clear the element contents after use.
In the polling model, Gestalt() can be used to obtain a pointer to the patch's routine through which it will provide the data. Then, calling that routine with an appropriate selector will result in the return of a struct containing the file info. An example of how to do this is presented in [Mark94]. A partial sample is presented in the next few listings.
Here is a sample data structure that can be used to hold the FSSpec data, plus a little more (such as the number of the trap that was called).
typedef struct FSPatchGlobals { short theTrapId; short theVRefNum; long theParID; Str63 theString; OSType theFileType; } FSPatchGlobals, *FSPatchGlobalsPtr;
For this example, we will use an array of structs to track the calls to _Create.
FSPatchGlobals gFSPatchGlobals[ kMaxNumFiles ];
Here is part of a Gestalt handler that can be used in the patch to respond to the background app's request for the next element.
static pascal OSErr FSPatchGestalt( OSType theSelector, long *theResponse ) { switch ( theSelector ) { case kGetNextElement: // Return the next element. *theResponse = ( long ) &gFSPatchGlobals[ gLastRetrievedElement ]; // Update the index. gLastRetrievedElement++; // Wrap to zero. if ( gLastRetrievedElement > kMaxNumFiles - 1 ) gLastRetrievedElement = 0; break; } }Listing 2. Using Gestalt() to return data.
The background app can find and call the patch Gestalt handler. First, find the handler address:
OSErr theErr; static pascal OSErr ( *FSPatchGestalt ) ( OSType selector, long *response ); theErr = Gestalt( kPatchSig, ( long * )&FSPatchGestalt );
Send the appropriate selector to request the next array element:
theErr = FSPatchGestalt( kGetNextElement, ( long * ) &gRAGlobalsPtr[ gLastProcessed ] ); gLastProcessed++;Listing 3. Using Gestalt() to request data.
Using a shared data area, the patch must first populate a structure or object, as shown in Listing 4. (An XFileWatcherRecord is similar, though not identical, to the FSPatchGlobals structure discussed previously.) The HParmBlkPtr is the source for info for the newly created file.
HParmBlkPtr pb; XFileWatcherRecord *rec = (XFileWatcherRecord*) PopAtomicStack( gInputStack ); if( rec ) { rec->rec.action = kCreatedFile; rec->rec.vRefNum = pb->fileParam.ioVRefNum; BlockMovePString( pb->fileParam.ioNamePtr, rec->rec.name1 ); rec->rec.dirID1 = pb->fileParam.ioDirID; PushAtomicQueue( (AtomicElement*) rec, gOutputQueue ); }Listing 4. Using a shared data area.
Now the background app must retrieve the next available (filled) structure or object from the shared data area. It can use, and then clear, the object before returning.
XFileWatcherRecord *rec = (XFileWatcherRecord*) PopAtomicQueue( gOutputQueue );
If you are not familiar with atomic queues, you should definitely read "Atomicity" [Rentzsch99] while you're here at the conference.
After receiving the raw data for a newly created file, the background app can obtain the path to that file using PBGetCatInfo().
If necessary, we can add our own timestamp using GetDateTime(), or query the OS for the creation time of the file using PBGetFInfo().
Those Toolbox calls are well-documented in Inside Macintosh, THINK Reference, etc. Plus, relevant examples abound in Macintosh programming books. Some relevant code fragments are provided in the next section.